﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NumeroComplejo
{
    /// <summary>
	/// <p>Representación de doble precision de un número complejo</p>
	/// </summary>
    public class Complejo : IComparable
    {
        /// <summary>
        /// El componente real de un número complejo
        /// </summary>
        private double real;
        /// <summary>
        /// El componente imaginario de un número complejo
        /// </summary>
        private double imaginario;

        public void establecerReal(double real)
        {
            this.real = real;
        }

        public double obtenerReal()
        {
            return this.real;
        }

        public void establecerImaginario(double imaginario)
        {
            this.imaginario = imaginario;
        }

        public double obtenerImaginario()
        {
            return this.imaginario;
        }

        /// <summary>
		/// Constructor sin argumentos
		/// </summary>
        public Complejo() {}

        /// <summary>
		/// Constructor con argumentos para la parte real y la parte imaginaria
		/// </summary>
        public Complejo(double real, double imaginario)
        {
			this.real = real;
			this.imaginario	= imaginario;
		}

        /// <summary>
        /// Convierte un numero real en un número complejo con la parte imaginaria igual a cero
        /// </summary>
        public Complejo(byte real)
        {
            this.real = real;
            this.imaginario = 0;
        }

        /// <summary>
        /// Convierte un numero real en un número complejo con la parte imaginaria igual a cero
        /// </summary>
        public Complejo(double real)
        {
            this.real = real;
            this.imaginario = 0;
        }

        /// <summary>
		/// Constructor crea un número complejo basado en uno ya existente como argumento
		/// </summary>
        public Complejo(Complejo c)
        {
			this.real = c.real;
			this.imaginario	= c.imaginario;
		}

        /// <summary>
        /// Crea un número complejo tomando una parte real y una imaginaria
        /// </summary>
        static public Complejo nuevoComplejoReIm(double real, double imaginario)
        {
            Complejo c = new Complejo(real,imaginario);
            return c;
        }

        /// <summary>
        /// Crea un número complejo tomando un módulo (longitud) y un argumento (radianes)
        /// </summary>
        static public Complejo nuevoComplejoModArg(double modulo, double argumento)
        {
            Complejo c = new Complejo(modulo * Math.Cos(argumento), modulo * Math.Sin(argumento));
            return c;
        }

        /// <summary>
        /// Clona un número complejo
        /// </summary>
        public Complejo Clone()
        {
            return new Complejo(this);
        }

        /// <summary>
        /// Módulo o valor absoluto del núemro complejo
        /// </summary>
        public double obtenerModulo()
        {
            double x = this.real;
            double y = this.imaginario;
            return Math.Sqrt(x * x + y * y);
        }

        /// <summary>
        /// Ángulo en radianes del número complejo
        /// </summary>
        public double obtenerArgumento()
        {
            return Math.Atan2(this.imaginario, this.real);
        }      

		/// <summary>
		/// Obtiene el conjugado de un número complejo
		/// </summary>
		/// <returns></returns>
		public Complejo obtenerConjugado() {
			return nuevoComplejoReIm( this.real, -this.imaginario );
		}

		/// <summary>
		/// Escala el número complejo a 1.
		/// </summary>
		public void Normalize() {
			double	modulo = this.obtenerModulo();
			if( modulo == 0 ) {
				throw new DivideByZeroException( "No se puede normalizar un número complejo que sea cero." );
			}
			this.real	= (double)( this.real / modulo );
			this.imaginario	= (double)( this.imaginario / modulo );
		}

		/// <summary>
		/// Compara dos números complejos, establece igualdad
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static bool	operator==( Complejo a, Complejo b ) {
			return	( a.real == b.real ) && ( a.imaginario == b.imaginario );
		}

		/// <summary>
        /// Compara dos números complejos, establece desigualdad
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static bool	operator!=( Complejo a, Complejo b ) {
			return	( a.real != b.real ) || ( a.imaginario != b.imaginario );
		}

		/// <summary>
		/// Obtiene el código hash de un número complejo
		/// </summary>
		/// <returns></returns>
		public override int		GetHashCode() {
			return	( this.real.GetHashCode() ^ this.imaginario.GetHashCode() );
		}

		/// <summary>
		/// Establece equivalencia entre el número complejo y otro objeto
		/// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
		public override bool	Equals( object o ) {
			if( o is Complejo ) {
				Complejo c = (Complejo) o;
				return   ( this == c );
			}
			return	false;
		}

		/// <summary>
		/// Compara con otros números complejos o relaes
        /// </summary>
		/// <param name="o"></param>
		/// <returns></returns>
		public int	CompareTo( object o ) {
			if( o == null ) {
				return 1;  
			}
			if( o is Complejo ) {
				return	this.obtenerModulo().CompareTo( ((Complejo)o).obtenerModulo() );
			}
			throw new ArgumentException();
		}

		/// <summary>
		/// Niega el número complejo
		/// </summary>
		/// <param name="a"></param>
		/// <returns></returns>
		public static Complejo operator-( Complejo a ) {
			a.real	= -a.real;
			a.imaginario = -a.imaginario;
			return a;
		}

		/// <summary>
		/// Suma un número complejo y uno real
		/// </summary>
		/// <param name="a"></param>
		/// <param name="f"></param>
		/// <returns></returns>
		public static Complejo operator+( Complejo a, double f ) {
            Complejo c = new Complejo(); 
			c.real	= (double)( a.real + f );
			return c;
		}

		/// <summary>
		/// Suma entre números complejos
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Complejo operator+( Complejo a, Complejo b ) {
            Complejo c = new Complejo(); 
			
            c.real	= a.real + b.real;
			c.imaginario = a.imaginario + b.imaginario;

			return c;
		}

		/// <summary>
		/// Resta un número real de un número complejo
		/// </summary>
		/// <param name="a"></param>
		/// <param name="f"></param>
		/// <returns></returns>
		public static Complejo operator-( Complejo a, double f ) {
            Complejo c = new Complejo(); 
            
            c.real	= (double)( a.real - f );

			return c;
		}

		/// <summary>
        /// Resta un número complejo de un número real  
		/// </summary>
		/// <param name="f"></param>
		/// <param name="a"></param>
		/// <returns></returns>
		public static Complejo operator-( double f, Complejo a ) {
            Complejo c = new Complejo(); 
			c.real	= (float)( f - a.real );
			c.imaginario	= (float)( 0 - a.imaginario );
			return c;
		}

		/// <summary>
        /// Resta entre dos números complejos 
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Complejo operator-( Complejo a, Complejo b ) {
            Complejo c = new Complejo(); 
			c.real	= a.real - b.real;
			c.imaginario = a.imaginario - b.imaginario;
			return c;
		}

		/// <summary>
        /// Multiplica un número complejo por uno real
		/// </summary>
		/// <param name="a"></param>
		/// <param name="f"></param>
		/// <returns></returns>
		public static Complejo operator*( Complejo a, double f ) {
            Complejo c = new Complejo(); 
            c.real	= (double)( a.real * f );
			c.imaginario = (double)( a.imaginario * f );
			return c;
		}
		
		/// <summary>
        /// Multiplica dos números complejos 
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Complejo operator*( Complejo a, Complejo b ) {
			// (x + yi)(u + vi) = (xu – yv) + (xv + yu)i. 
			double	x = a.real, y = a.imaginario;
			double	u = b.real, v = b.imaginario;

            Complejo c = new Complejo(); 

			c.real	= (double)( x*u - y*v );
			c.imaginario = (double)( x*v + y*u );
			
			return c;
		}

		/// <summary>
        /// Divide un número complejo por uno real 
		/// </summary>
		/// <param name="a"></param>
		/// <param name="f"></param>
		/// <returns></returns>
		public static Complejo operator/( Complejo a, double f ) {
			if( f == 0 ) {
				throw new DivideByZeroException();
			}
			
			a.real	= (double)( a.real / f );
			a.imaginario = (double)( a.imaginario / f );
			
			return a;
		}
		
		/// <summary>
        /// Divide dos números complejos
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static Complejo operator/( Complejo a, Complejo b ) {
			double	x = a.real,	y = a.imaginario;
			double	u = b.real,	v = b.imaginario;
			double	denominador = u*u + v*v;

			if( denominador == 0 ) {
				throw new DivideByZeroException();
			}

			a.real	= (double)( ( x*u + y*v ) / denominador );
			a.imaginario	= (double)( ( y*u - x*v ) / denominador );
			
			return a;
		}
		
		/// <summary>
		/// Obtiene la representacion en cadena de caracteres del número complejo 
		/// </summary>
		/// <returns></returns>
		public override string ToString() {
			return	String.Format( "( {0}, {1}i )", this.real, this.imaginario );
		}

		/// <summary>
		/// Determina equivalencia con un margen de error.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <param name="tolerancia"></param>
		/// <returns></returns>
		static public bool IsEqual( Complejo a, Complejo b, double tolerancia ) {
			return ( Math.Abs( a.real - b.real ) < tolerancia ) &&	( Math.Abs( a.imaginario - b.imaginario ) < tolerancia );
		}

		/// <summary>
		/// Representa el cero
		/// </summary>
		static public Complejo cero {
			get	{	return	new Complejo( 0, 0 );	}
		}

		/// <summary>
		/// Representa el resultado de raiz cuadrada de -1, sqrt(-1)
		/// </summary>
		static public Complejo	I {
			get {	return new Complejo( 0, 1 );	}
		}

		/// <summary>
		/// Representa el valor más grande posible de Complejo.
		/// </summary>
		static public Complejo MaxValue 
        {
			get {	return new Complejo( double.MaxValue, double.MaxValue );	}
		}

		/// <summary>
        /// Representa el valor más pequeño posible de Complejo.
		/// </summary>
		static public Complejo MinValue 
        {
			get {	return	new Complejo( double.MinValue, double.MinValue );	}
		}
	}
}
